﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Pavilion
{
    public class LinkMonitor
    {
        public event Action<LinkMonitor> LinkEstablished;
        public event EventHandler Disposed;
        public Dictionary<ILock, LockMonitorBase> LockMonitorList { get; private set; }
        public Link MonitoredLink { get; private set; }

        private GameItem gameItem;

        public LinkMonitor(Link linkToMonitor, GameItem gameItem)
        {
            this.MonitoredLink = linkToMonitor;
            this.MonitoredLink.Disposed += new EventHandler(MonitoredLink_Disposed);
            this.MonitoredLink.LockAdded += new Action<Link, ILock>(MonitoredLink_LockAdded);
            this.MonitoredLink.LockRemoved += new Action<Link, ILock>(MonitoredLink_LockRemoved);

            LockMonitorList = new Dictionary<ILock, LockMonitorBase>();

            this.gameItem = gameItem;
            CreateLockMonitors();
        }

        public void Dispose()
        {
            this.MonitoredLink.Disposed -= new EventHandler(MonitoredLink_Disposed);
            this.MonitoredLink.LockAdded -= new Action<Link, ILock>(MonitoredLink_LockAdded);
            this.MonitoredLink.LockRemoved -= new Action<Link, ILock>(MonitoredLink_LockRemoved);
            MonitoredLink = null;

            RemoveLockMonitors();
            LockMonitorList = null;

            OnDisposed();
        }

        public void CreateLockMonitors()
        {
            foreach (ILock lockItem in this.MonitoredLink.LockCollection)
                CreateSingleLockMonitor(lockItem);
        }

        private void RemoveLockMonitors()
        {
            while (0 < LockMonitorList.Count)
                RemoveSingleLockMonitor(LockMonitorList.Keys.First());
        }

        private LockMonitorBase CreateSingleLockMonitor(ILock lockItem)
        {
            GameItem gameItem = this.gameItem;

            if (!string.IsNullOrEmpty(lockItem.GameItemName))
                gameItem = GameItemManager.Instance[lockItem.GameItemName];

            LockMonitorBase lockMonitor = lockItem.CreateLockMonitor(gameItem);
            LockMonitorList.Add(lockItem, lockMonitor);
            lockMonitor.Load(gameItem);
            lockMonitor.LockOpened += new Action<LockMonitorBase>(LockMonitor_LockOpened);

            return lockMonitor;
        }

        private void RemoveSingleLockMonitor(ILock lockItem)
        {
            LockMonitorList[lockItem].LockOpened -= new Action<LockMonitorBase>(LockMonitor_LockOpened);
            LockMonitorList[lockItem].Dispose();
            LockMonitorList.Remove(lockItem);
        }

        public bool IsLinkEstablished
        {
            get
            {
                if (MonitoredLink.OpenCriteria == OpenCriteria.All)
                    return AreAllLocksOpen();
                if (MonitoredLink.OpenCriteria == OpenCriteria.Single)
                    return IsSingleLockOpen();

                return true;
            }
        }

        private bool AreAllLocksOpen()
        {
            foreach (LockMonitorBase lockMonitor in LockMonitorList.Values)
            {
                if (!lockMonitor.IsOpen)
                    return false;
            }

            return true;
        }

        private bool IsSingleLockOpen()
        {
            if (LockMonitorList.Count == 0)
                return true;

            foreach (LockMonitorBase lockMonitor in LockMonitorList.Values)
            {
                if (lockMonitor.IsOpen)
                    return true;
            }

            return false;
        }

        private void OnLinkEstablished()
        {
            if (LinkEstablished != null)
                LinkEstablished(this);
        }

        private void OnDisposed()
        {
            if (Disposed != null)
                Disposed(this, EventArgs.Empty);
        }

        private void LockMonitor_LockOpened(LockMonitorBase lockMonitor)
        {
            if (MonitoredLink.OpenCriteria == OpenCriteria.Single)
                OnLinkEstablished();
            else if (MonitoredLink.OpenCriteria == OpenCriteria.All)
            {
                if (AreAllLocksOpen())
                    OnLinkEstablished();
            }
        }

        private void MonitoredLink_Disposed(object sender, EventArgs e)
        {
            this.Dispose();
        }

        private void MonitoredLink_LockAdded(Link link, ILock lockItem)
        {
            LockMonitorBase lockMonitor = CreateSingleLockMonitor(lockItem);

            if (lockMonitor.IsOpen)
                LockMonitor_LockOpened(lockMonitor);
        }

        private void MonitoredLink_LockRemoved(Link link, ILock lockItem)
        {
            RemoveSingleLockMonitor(lockItem);

            if (IsLinkEstablished)
                OnLinkEstablished();
        }
    }
}
